<?php
/**
 * Project: HTPHouDuan
 * User: xilibo
 * Date: 2018/1/24
 */

namespace app\common\library;
use think\Exception;

/**
 * mcrypt加密类
 */
class Mcrypter
{
    // 密钥
    protected $secretKey = '';

    // 初始向量
    protected $iv = '';

    // 加密算法
    protected $algorithm = MCRYPT_3DES;

    // 加密模式
    protected $mode = MCRYPT_MODE_ECB;

    // 填充类型
    protected $padType = 'pkcs7';

    protected static $instance = null;

    public function __construct($config)
    {
        foreach ($config as $key => $value) {
            if (property_exists($this, $key)){
                $this->$key = $value;
            }
        }

        if(is_null($this->secretKey)){
            throw new \Exception('密钥不能为空');
        }

        $this->secretKey = str_pad($this->secretKey, 24, '0');
    }

    public static function instance($config)
    {
        if (is_null(self::$instance)){
            self::$instance = new self($config);
        }

        return self::$instance;
    }

    protected function padOrUnpad($str, $ext)
    {
        if (!is_null($this->padType)) {
            $classMethod = __CLASS__ . '::' . $this->padType . ucfirst($ext . 'pad');
            if (is_callable($classMethod)) {
                $size = mcrypt_get_block_size($this->algorithm, $this->mode);
                return call_user_func($classMethod, $str, $size);
            }
        }
        
        return $str;
    }

    protected function pad($str)
    {
        return $this->padOrUnpad($str, '');
    }

    protected function unpad($str)
    {
        return $this->padOrUnpad($str, 'un');
    }

    public function encrypt($str)
    {
        $str = $this->pad($str);
        $td = mcrypt_module_open($this->algorithm, '', $this->mode, '');
        
        if (empty($this->iv)) {
            $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        } else {
            $iv = $this->iv;
        }

        mcrypt_generic_init($td, $this->secretKey, $iv);
        $encryptedText = mcrypt_generic($td, $str);
        $encryptedText = base64_encode($encryptedText);
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);

        return $encryptedText;
    }

    public function decrypt($str)
    {
        $td = mcrypt_module_open($this->algorithm, '', $this->mode, '');
        
        if (empty($this->iv)) {
            $iv = @mcrypt_create_iv(mcrypt_enc_get_iv_size($td), MCRYPT_RAND);
        } else {
            $iv = $this->iv;
        }
        
        mcrypt_generic_init($td, $this->secretKey, $iv);
        $decryptedText = mdecrypt_generic($td, base64_decode($str));
        $rt = $decryptedText;
        mcrypt_generic_deinit($td);
        mcrypt_module_close($td);
        
        return $this->unpad($rt);
    }

    public static function pkcs7Pad($text, $blocksize)
    {
        $pad = $blocksize - (strlen($text) % $blocksize);

        return $text . str_repeat(chr($pad), $pad);
    }

    public static function pkcs7Unpad($text)
    {
        $pad = ord($text[strlen($text) - 1]);

        if ($pad > strlen($text)) {
            return false;
        }

        if (strspn($text, chr($pad), strlen($text) - $pad) != $pad) {
            return false;
        }

        return substr($text, 0, -1 * $pad);
    }


}
